package com.leyou.search.service.impl;


import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.leyou.exception.LyException;
import com.leyou.item.client.ItemClient;
import com.leyou.item.dto.CategoryDTO;
import com.leyou.search.dto.SearchDTO;
import com.leyou.search.pojo.Goods;
import com.leyou.search.repository.GoodsRepository;

import com.leyou.search.service.SearchService;
import com.leyou.starter.elastic.entity.PageInfo;
import org.apache.commons.lang3.StringUtils;
import org.apache.lucene.index.Term;
import org.elasticsearch.index.query.MatchQueryBuilder;
import org.elasticsearch.index.query.Operator;
import org.elasticsearch.index.query.QueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.aggregations.Aggregation;
import org.elasticsearch.search.aggregations.AggregationBuilders;
import org.elasticsearch.search.aggregations.Aggregations;
import org.elasticsearch.search.aggregations.bucket.nested.Nested;
import org.elasticsearch.search.aggregations.bucket.nested.NestedAggregator;
import org.elasticsearch.search.aggregations.bucket.terms.Terms;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import reactor.core.publisher.Mono;

import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

@Service
public class SearchServiceImpl implements SearchService {

    @Autowired
    private GoodsRepository goodsRepository;

    @Autowired
    private ItemClient itemClient;

    @Override
    public Mono<List<String>> getSugg(String key) {

        return this.goodsRepository.suggestBySingleField("suggestion", key);
    }

    @Override
    public Mono<PageInfo<Goods>> listData(SearchDTO searchDTO) {

        SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();

        //添加查询
        sourceBuilder.query(builderQuery(searchDTO));

        HighlightBuilder builder = new HighlightBuilder();

        builder.preTags("<am>");
        builder.postTags("</am>");
        builder.field("title");

        sourceBuilder.highlighter(builder);

        //字段过滤
        sourceBuilder.fetchSource(new String[]{"id", "title", "image", "categoryId", "brandId", "prices", "sold"}, null);

        Integer size = searchDTO.getSIZE();
        int from = (searchDTO.getPage() - 1) * size;

        //
        sourceBuilder.from(from);

        sourceBuilder.size(size);


        return this.goodsRepository.queryBySourceBuilderForPageHighlight(sourceBuilder);
    }

    private QueryBuilder builderQuery(SearchDTO searchDTO) {
        String key = searchDTO.getKey();
        if (StringUtils.isBlank(key)) {
            throw new LyException(400, "查询条件不能为空");
        }
        return QueryBuilders.matchQuery("title", key).operator(Operator.AND);
    }

    @Override
    public Mono<Map<String, List<?>>> listFilter(SearchDTO searchDTO) {

        SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();

        //添加查询添加
        sourceBuilder.query(builderQuery(searchDTO));

        //添加聚合条件
        sourceBuilder.aggregation(AggregationBuilders.terms("brandAgg").field("brandId"));
        sourceBuilder.aggregation(AggregationBuilders.terms("categoryAgg").field("categoryId"));

        //其他可搜索规格参数的聚合,先做nested聚合然后再做name聚合，在name中做value聚合

        sourceBuilder.aggregation(
                AggregationBuilders.nested("specAgg", "specs")
                        .subAggregation(
                                (AggregationBuilders.terms("nameAgg").field("specs.name"))
                                        .subAggregation(AggregationBuilders.terms("valueAgg").field("specs.value"))
                        )

        );
        //

        //聚合查询
        Mono<Aggregations> aggregationsMono = this.goodsRepository.aggregationBySourceBuilder(sourceBuilder);


        //动态处理Mono的内容
        //解析聚合
        return aggregationsMono.map(aggregations -> {

            Map<String, List<?>> result = new LinkedHashMap<>();


            Terms categoryAgg = aggregations.get("categoryAgg");

            List<Long> categoryIds = categoryAgg.getBuckets()
                    .stream()
                    .map(Terms.Bucket::getKeyAsNumber)
                    .map(Number::longValue)
                    .collect(Collectors.toList());

            //判断id集合是否为空
            if (CollectionUtils.isEmpty(categoryIds)) {
                result.put("分类", new ArrayList<>());
            } else {
                result.put("分类", this.itemClient.queryCategoryByIds(categoryIds));
            }


            Terms brandAgg = aggregations.get("brandAgg");

            List<Long> brandIds = brandAgg.getBuckets()
                    .stream()
                    .map(Terms.Bucket::getKeyAsNumber)
                    .map(Number::longValue)
                    .collect(Collectors.toList());

            //判断id集合是否为空
            if (CollectionUtils.isEmpty(brandIds)) {
                result.put("品牌", new ArrayList<>());
            } else {
                result.put("品牌", this.itemClient.queryBrandByIds(brandIds));
            }


            Nested specAgg = aggregations.get("specAgg");

            Terms nameAgg = specAgg.getAggregations().get("nameAgg");

            //循环获取聚合的key存入result
            nameAgg.getBuckets().forEach(bucket -> {
                String key = bucket.getKeyAsString();

                //根据valueAgg名称获取每一个nameAgg中的内容
                Terms valueTerms = bucket.getAggregations().get("valueAgg");

                //从分桶中获取其value聚合的每个key然后处理成集合
                List<String> valueList = valueTerms.getBuckets()
                        .stream()
                        .map(Terms.Bucket::getKeyAsString)
                        .collect(Collectors.toList());

                result.put(key, valueList);
            });


            return result;
        });
    }
}
